/*
 * Includes
 */
#include <stdio.h>
#include <stdlib.h>
#include <minix/drivers.h>
#include <minix/driver.h>
#include <minix/safecopies.h>
#include "ctrl.h"

#define DEV_NAME "ctrl"
#define OK 0

/*
 * Device functions
 */
FORWARD _PROTOTYPE(char *ctrl_name, (void));
FORWARD _PROTOTYPE(int ctrl_open, (struct driver *d, message *m));
FORWARD _PROTOTYPE(int ctrl_close, (struct driver *d, message *m));
FORWARD _PROTOTYPE(int do_receive_message, (struct driver *d, message *m));
FORWARD _PROTOTYPE(struct device *ctrl_prepare, (int d));
FORWARD _PROTOTYPE(int ctrl_transfer,
		(endpoint_t endpoint, int opcode, u64_t position, iovec_t *vector,
				unsigned nr_req, endpoint_t UNUSED( user_endpoint),
				unsigned int UNUSED( flags)));
FORWARD _PROTOTYPE(void ctrl_geometry, (struct partition *entry));

/*
 * SEF prototypes
 */
FORWARD _PROTOTYPE(void sef_local_startup, (void));
FORWARD _PROTOTYPE(int sef_cb_init, (int type, sef_init_info_t *info));

/*
 * Device entrypoint
 */
static struct driver ctrl_tab = { ctrl_name, ctrl_open, ctrl_close, nop_ioctl,
		ctrl_prepare, ctrl_transfer, nop_cleanup, ctrl_geometry, nop_alarm,
		nop_cancel, nop_select, do_receive_message,
		/*nop_ioctl,*/
		do_nop, };

PRIVATE int do_receive_message(struct driver *d, message *m) {

	printf("receiving messages in ctrl driver\n");

	return OK;
}

/*
 * Represents the /dev/ctrl device
 */
PRIVATE struct device ctrl_device;

/*
 * Returns the device name
 */
PRIVATE char *ctrl_name() {
	return DEV_NAME;
}

/*
 * Opens the device
 */
PRIVATE int ctrl_open(struct driver *d, message *m) {
	return OK;
}

/*
 * Close the device
 */
PRIVATE int ctrl_close(struct driver *d, message *m) {
	printf("closing the ctrl driver");
	return OK;
}

/*
 * Prepares the device
 */
PRIVATE struct device *ctrl_prepare(int d) {
	ctrl_device.dv_base.lo = 0;
	ctrl_device.dv_base.hi = 0;
	ctrl_device.dv_size.lo = 0;
	ctrl_device.dv_size.hi = 0;

	return &ctrl_device;
}

/*
 * Performs the read/write of the device
 */
PRIVATE int ctrl_transfer(endpoint_t endpoint, int opcode, u64_t position,
		iovec_t *vector, unsigned nr_req, endpoint_t UNUSED( user_endpoint),
		unsigned int UNUSED( flags)) {
	/* Variables */
	int bytes;
	int ret;
	char buffer[32];
	char *argument;
	char *arguments[15];
	int i;
	int argc = 0;
	printf("ctrl_transfer\n");
	bytes = strlen(buffer) > vector->iov_size ?
			strlen(buffer) : vector->iov_size;

	if (bytes <= 0) {
		return OK;
	} else {
		switch (opcode) {
		case DEV_GATHER_S:
			printf("Entered DEV_GATHER_S\n");
			break;

		case DEV_SCATTER_S:
			printf("Entered DEV_SCATTER_S\n");
			ret = sys_safecopyfrom(endpoint, vector->iov_addr, 0,
					(vir_bytes)(buffer), bytes, D);
			break;

		default:
			return EINVAL;
			break;
		}
	}
	return ret;
}

PRIVATE void ctrl_geometry(struct partition *entry) {
	entry->cylinders = 0;
	entry->heads = 0;
	entry->sectors = 0;
}

/*
 * Runs the program
 */
PUBLIC int main(int argc, char* argv[]) {

	message ctrl_mess; /* buffer for all incoming messages */
	int r, i;
	int endpoints[NO_RT_DEVICES];
	int sendreply;
	char buffer[32];
	char *argument;
	char *arguments[15];
	int arguments_counter = 0;
	endpoints[0] = 0;/*cs*/
	endpoints[1] = 0;/*com*/
	endpoints[2] = 0;/*cl*/

	/* Initialize SEF */
	sef_local_startup();

	while (TRUE) {
		/*receive message*/
		r = sef_receive(ANY, &ctrl_mess);
		if (r != 0)
			panic("driver_receive failed with: %d", r);

		switch (ctrl_mess.m_type) {
		case SEND_ENDPOINT: {
			sendreply = ctrl_mess.m_source == PM_PROC_NR ? TRUE : FALSE; /*Only send reply if message is received from pm and not directly*/
			endpoints[ctrl_mess.RT_DRIVER_ID] =
					ctrl_mess.m_source == PM_PROC_NR ?
							ctrl_mess.RT_DRIVER_ENDPT : ctrl_mess.m_source;/*If send from pm, save the msg endpoint*/
			if (sendreply) {/*Send reply*/
				int s;
				message m;
				m.m_type = SEND_ENDPOINT;
				m.RT_DRIVER_ID = RT_DEV_CTRL;
				m.RT_DRIVER_ENDPT = 0; /*Announce own endpoint*/
				if (OK != (s = send(endpoints[ctrl_mess.RT_DRIVER_ID], &m))) {
					panic(
							"Did not send endpoint from CTRL driver to COM driver.\n");
				}
			}

		}
			break;
		case RS232_TO_CTRL: {/*receive data from rs232 driver*/
			int s;
			message m;
			if (endpoints[RT_DEV_COM] != 0) {
				/*send message with value to communication driver*/
				m.m_type = CTRL_TO_COM;
				m.RS232_CTRL = ctrl_mess.RS232_CTRL;
				if (OK != (s = send(endpoints[RT_DEV_COM], &m))) {
					panic("Did not send endpoint from rs232 to ctrl\n");
				}
			} else {
				printf("Endpoint com:%d\n", endpoints[RT_DEV_COM]);
			}
		}
			break;
		default:
			printf("Warning, ctrl driver got unexpected request \n");
		}

	}

	/* Run the main loop */
	driver_task(&ctrl_tab, DRIVER_STD);
	return OK;
}

/*
 * Sets the SEF calls
 */
PRIVATE void sef_local_startup() {
	/* Register init call backs */
	sef_setcb_init_fresh(sef_cb_init);
	sef_setcb_init_lu(sef_cb_init);
	sef_setcb_init_restart(sef_cb_init);

	/* Register live update call backs. */
	sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
	sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);

	/* Startup SEF */
	sef_startup();
}

/*
 * Handles all the SEF calls
 */
PRIVATE int sef_cb_init(int type, sef_init_info_t *info) {
	int do_announce_driver = TRUE;

	switch (type) {
	case SEF_INIT_FRESH: {
		/* Initialise the driver */
		int s;
		message m;
		m.m_type = PM_DRIVER_INIT;
		m.RT_DRIVER_ID = RT_DEV_CTRL;
		if (OK != (s = send(PM_PROC_NR, &m))) {
			panic("Driver init msg did not send\n");
		}
		printf("%s: driver is initialized.\n", PROGRAM_NAME);
	}
		break;

	case SEF_INIT_LU:
		printf("%s: driver is updated.\n", PROGRAM_NAME);
		do_announce_driver = FALSE;
		break;

	case SEF_INIT_RESTART:
		printf("%s: driver is restarted.\n", PROGRAM_NAME);
		break;
	}

	if (do_announce_driver) {
		driver_announce();
	}

	/* Driver initialization successful */
	return OK;
}
